From e65252afaa61067a2700eb78d890b9a46cefaedb Mon Sep 17 00:00:00 2001
From: Joshua Ashton <joshua@froggi.es>
Date: Wed, 20 Nov 2019 19:02:08 +0000
Subject: [PATCH] winex11: Implement true integer scaling

---
 dlls/winevulkan/vulkan.c         | 10 +++++-----
 dlls/winevulkan/vulkan_private.h |  1 +
 dlls/winex11.drv/opengl.c        |  6 +++++-
 dlls/winex11.drv/settings.c      | 27 ++++++++++++++++++++++++++-
 dlls/winex11.drv/vulkan.c        |  5 ++++-
 dlls/winex11.drv/x11drv.h        |  1 +
 include/wine/vulkan_driver.h     |  2 +-
 7 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c
index 58720d41f0b..63665fb7a6f 100644
--- a/dlls/winevulkan/vulkan.c
+++ b/dlls/winevulkan/vulkan.c
@@ -1267,7 +1267,7 @@ VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice
         capabilities->maxImageCount = 32;
 
     if(vk_funcs->query_fs_hack &&
-            vk_funcs->query_fs_hack(NULL, &user_res, NULL)){
+            vk_funcs->query_fs_hack(NULL, &user_res, NULL, NULL)){
         capabilities->currentExtent = user_res;
         capabilities->minImageExtent = user_res;
         capabilities->maxImageExtent = user_res;
@@ -1715,7 +1715,7 @@ VkResult WINAPI wine_vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCrea
         our_createinfo.oldSwapchain = ((struct VkSwapchainKHR_T *)(UINT_PTR)our_createinfo.oldSwapchain)->swapchain;
 
     if(vk_funcs->query_fs_hack &&
-            vk_funcs->query_fs_hack(&object->real_extent, &user_sz, &object->blit_dst) &&
+            vk_funcs->query_fs_hack(&object->real_extent, &user_sz, &object->blit_dst, &object->fs_hack_filter) &&
             our_createinfo.imageExtent.width == user_sz.width &&
             our_createinfo.imageExtent.height == user_sz.height)
     {
@@ -1946,8 +1946,8 @@ static VkResult init_blit_images(VkDevice device, struct VkSwapchainKHR_T *swapc
     uint32_t blit_memory_type = -1, i;
 
     samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
-    samplerInfo.magFilter = VK_FILTER_LINEAR;
-    samplerInfo.minFilter = VK_FILTER_LINEAR;
+    samplerInfo.magFilter = swapchain->fs_hack_filter;
+    samplerInfo.minFilter = swapchain->fs_hack_filter;
     samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
     samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
     samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
@@ -2494,7 +2494,7 @@ static VkResult record_graphics_cmd(VkDevice device, struct VkSwapchainKHR_T *sw
     device->funcs.p_vkCmdBlitImage(hack->cmd,
             hack->user_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
             hack->swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-            1, &blitregion, VK_FILTER_LINEAR /* CUBIC_IMG? */);
+            1, &blitregion, swapchain->fs_hack_filter);
 
     /* transition user image from TRANSFER_SRC_OPTIMAL to GENERAL */
     barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h
index 145635eea24..00626eff1e3 100644
--- a/dlls/winevulkan/vulkan_private.h
+++ b/dlls/winevulkan/vulkan_private.h
@@ -165,6 +165,7 @@ struct VkSwapchainKHR_T
     VkDeviceMemory user_image_memory, blit_image_memory;
     uint32_t n_images;
     struct fs_hack_image *fs_hack_images; /* struct fs_hack_image[n_images] */
+    VkFilter fs_hack_filter;
     VkSampler sampler;
     VkDescriptorPool descriptor_pool;
     VkDescriptorSetLayout descriptor_set_layout;
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c
index dad5279ba4f..0927991b93a 100644
--- a/dlls/winex11.drv/opengl.c
+++ b/dlls/winex11.drv/opengl.c
@@ -211,6 +211,7 @@ struct wgl_context
     struct gl_drawable *new_drawables[2];
     BOOL refresh_drawables;
     BOOL fs_hack;
+    BOOL fs_hack_integer;
     GLuint fs_hack_fbo, fs_hack_resolve_fbo;
     GLuint fs_hack_color_texture, fs_hack_ds_texture;
     GLuint fs_hack_color_renderbuffer, fs_hack_color_resolve_renderbuffer, fs_hack_ds_renderbuffer;
@@ -2066,6 +2067,7 @@ static void fs_hack_setup_context( struct wgl_context *ctx, struct gl_drawable *
 
         ctx->setup_for = p;
         gl->has_scissor_indexed = has_extension(glExtensions, "GL_ARB_viewport_array");
+        ctx->fs_hack_integer = fs_hack_is_integer();
         gl->fs_hack_context_set_up = TRUE;
     }
     else
@@ -2358,7 +2360,9 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer
     opengl_funcs.gl.p_glClear( GL_COLOR_BUFFER_BIT );
     opengl_funcs.gl.p_glClearColor( prev_clear_color[0], prev_clear_color[1], prev_clear_color[2], prev_clear_color[3] );
 
-    pglBlitFramebuffer( 0, 0, src.x, src.y, scaled_origin.x, scaled_origin.y, scaled_origin.x + scaled.x, scaled_origin.y + scaled.y, GL_COLOR_BUFFER_BIT, GL_LINEAR );
+    pglBlitFramebuffer( 0, 0, src.x, src.y,
+            scaled_origin.x, scaled_origin.y, scaled_origin.x + scaled.x, scaled_origin.y + scaled.y,
+            GL_COLOR_BUFFER_BIT, ctx->fs_hack_integer ? GL_NEAREST : GL_LINEAR );
     //HACK
     if ( draw_buffer == GL_FRONT )
         pglXSwapBuffers(gdi_display, gl->drawable);
diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c
index 0490df719a7..37d0c560bee 100644
--- a/dlls/winex11.drv/settings.c
+++ b/dlls/winex11.drv/settings.c
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <math.h>
+#include <stdlib.h>
 
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
@@ -274,6 +275,17 @@ BOOL fs_hack_enabled(void)
         currentMode != realMode;
 }
 
+BOOL fs_hack_is_integer(void)
+{
+    static int is_int = -1;
+    if(is_int < 0)
+    {
+        const char *e = getenv("WINE_FULLSCREEN_INTEGER_SCALING");
+        is_int = e && strcmp(e, "0");
+    }
+    return is_int;
+}
+
 BOOL fs_hack_matches_current_mode(int w, int h)
 {
     return fs_hack_enabled() &&
@@ -411,7 +423,20 @@ static LONG X11DRV_nores_SetCurrentMode(int mode)
     }else{
         double w = dd_modes[currentMode].width;
         double h = dd_modes[currentMode].height;
-        if(dd_modes[realMode].width / (double)dd_modes[realMode].height < w / h){ /* real mode is narrower than fake mode */
+
+        if(fs_hack_is_integer()){
+            unsigned int scaleFactor = min(dd_modes[realMode].width / w,
+                                           dd_modes[realMode].height / h);
+
+            w *= scaleFactor;
+            h *= scaleFactor;
+
+            offs_x = (dd_modes[realMode].width  - w) / 2;
+            offs_y = (dd_modes[realMode].height - h) / 2;
+
+            fs_width  = dd_modes[realMode].width;
+            fs_height = dd_modes[realMode].height;
+        }else if(dd_modes[realMode].width / (double)dd_modes[realMode].height < w / h){ /* real mode is narrower than fake mode */
             /* scale to fit width */
             h = dd_modes[realMode].width * (h / w);
             w = dd_modes[realMode].width;
diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c
index 2e2ac8f0b40..7b4c39bee31 100644
--- a/dlls/winex11.drv/vulkan.c
+++ b/dlls/winex11.drv/vulkan.c
@@ -545,7 +545,7 @@ static VkResult X11DRV_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *
 }
 
 static VkBool32 X11DRV_query_fs_hack(VkExtent2D *real_sz, VkExtent2D *user_sz,
-        VkRect2D *dst_blit)
+        VkRect2D *dst_blit, VkFilter *filter)
 {
     if(fs_hack_enabled()){
         POINT real_res = fs_hack_real_mode();
@@ -553,6 +553,9 @@ static VkBool32 X11DRV_query_fs_hack(VkExtent2D *real_sz, VkExtent2D *user_sz,
         POINT scaled = fs_hack_get_scaled_screen_size();
         POINT scaled_origin = {0, 0};
 
+        if(filter)
+            *filter = fs_hack_is_integer() ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
+
         fs_hack_user_to_real(&scaled_origin);
 
         if(real_sz){
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index d1918153e32..239e72d4b88 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -612,6 +612,7 @@ extern BOOL update_clipboard( HWND hwnd ) DECLSPEC_HIDDEN;
 extern void set_wm_hints( struct x11drv_win_data *data ) DECLSPEC_HIDDEN;
 extern BOOL fs_hack_enabled(void) DECLSPEC_HIDDEN;
 extern BOOL fs_hack_mapping_required(void) DECLSPEC_HIDDEN;
+extern BOOL fs_hack_is_integer(void) DECLSPEC_HIDDEN;
 extern BOOL fs_hack_matches_current_mode(int w, int h) DECLSPEC_HIDDEN;
 extern BOOL fs_hack_matches_real_mode(int w, int h) DECLSPEC_HIDDEN;
 extern POINT fs_hack_current_mode(void) DECLSPEC_HIDDEN;
diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h
index 40ff8c35dbd..0f2bae9b9ef 100644
--- a/include/wine/vulkan_driver.h
+++ b/include/wine/vulkan_driver.h
@@ -71,7 +71,7 @@ struct vulkan_funcs
      * resolution; user_sz will contain the app's requested mode; and dst_blit
      * will contain the area to blit the user image to in real coordinates.
      * All parameters are optional. */
-    VkBool32 (*query_fs_hack)(VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit);
+    VkBool32 (*query_fs_hack)(VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter);
 };
 
 extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);
From 0f22d04d80db76054302251d7eccc39414dbcab4 Mon Sep 17 00:00:00 2001
From: Georg Lehmann <dadschoorse@gmail.com>
Date: Wed, 4 Mar 2020 13:42:04 +0100
Subject: [PATCH] winevulkan: fix missing VkFilter for fshack integer scaling
 in vulkan_driver.h

---
 dlls/winevulkan/make_vulkan | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan
index a49cd2409c9..10728785223 100755
--- a/dlls/winevulkan/make_vulkan
+++ b/dlls/winevulkan/make_vulkan
@@ -2512,7 +2512,7 @@ class VkGenerator(object):
         f.write("     * resolution; user_sz will contain the app's requested mode; and dst_blit\n")
         f.write("     * will contain the area to blit the user image to in real coordinates.\n")
         f.write("     * All parameters are optional. */\n")
-        f.write("    VkBool32 (*query_fs_hack)(VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit);\n")
+        f.write("    VkBool32 (*query_fs_hack)(VkExtent2D *real_sz, VkExtent2D *user_sz, VkRect2D *dst_blit, VkFilter *filter);\n")
 
         f.write("};\n\n")
 
